#include "ChatWindow.h"
#include "Tools.h"
#include "RAGSearch.h"

#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>

#include <QJsonArray>
#include <QJsonObject>
#include <QSizePolicy>
#include <QApplication>

#include <iostream>

#include <QHBoxLayout>
#include <QListWidgetItem>

using namespace std;

ChatWindow::ChatWindow(QWidget *parent)
    : QWidget(parent)
{

    // 创建滚动区域
    scrollArea = new QScrollArea(this);
    scrollArea->setWidgetResizable(true); // 确保滚动区域可以调整大小

    // 创建一个 QWidget 作为滚动区域的内容
    scrollWidget = new QWidget(this);
    scrollWidget->setLayout(new QVBoxLayout()); // 为 scrollWidget 设置布局管理器

    scrollArea->setWidget(scrollWidget);

    // 获取 scrollWidget 的布局并设置为 chatDisplay
    chatDisplay = qobject_cast<QVBoxLayout *>(scrollWidget->layout());
    chatDisplay->setSpacing(10);             // 设置气泡之间的间距
    chatDisplay->setAlignment(Qt::AlignTop); // 确保气泡从上到下排列

    inputLine = new QLineEdit(this);
    sendButton = new QPushButton("发送", this);
    newChatButton = new QPushButton("新建聊天", this);
    newChatButton->setIcon(QIcon("../assets/消息-新建聊天TOP.png"));
    newChatButton->setIconSize(QSize(30, 30));

    voiceButton = new QPushButton(this);
    voiceButton->setIcon(QIcon("../assets/麦克风.png")); // 设置语音按钮图标路径

    ragButton = new QPushButton("RAG", this);
    ragButton->setStyleSheet("background-color:rgb(243, 240, 240); color: black;"); // 浅色状态

    historyList = new QListWidget(this);
    selectedFileList = new QListWidget(this);

    QHBoxLayout *totalLayout = new QHBoxLayout(this);

    QVBoxLayout *leftLayout = new QVBoxLayout;

    QVBoxLayout *rightLayout = new QVBoxLayout;

    QHBoxLayout *rightdownLayout = new QHBoxLayout;

    totalLayout->addLayout(leftLayout);
    totalLayout->addLayout(rightLayout);

    // 设置左右布局的比例
    totalLayout->setStretchFactor(leftLayout, 1);  // 左侧布局占据 1 份
    totalLayout->setStretchFactor(rightLayout, 3); // 右侧布局占据 3 份

    leftLayout->addWidget(newChatButton);
    leftLayout->addWidget(historyList);
    leftLayout->addWidget(selectedFileList);

    // 将滚动区域添加到右侧布局
    rightLayout->addWidget(scrollArea);
    rightLayout->addLayout(rightdownLayout);

    rightdownLayout->addWidget(inputLine);
    rightdownLayout->addWidget(ragButton);
    rightdownLayout->addWidget(voiceButton);
    rightdownLayout->addWidget(sendButton);

    // 添加一个初始的气泡
    addChatBubble("你好，请问有什么需要我帮忙的吗？", false);

    adjustComponentSizes();

    setLayout(totalLayout);
    setWindowTitle("模型聊天");

    // 设置窗口图标
    setWindowIcon(QIcon("../assets/智能机器人.png"));

    resize(800, 600);

    move(500, 500);

    // 初始化语音模块
    voiceModule = new ListenAndSpeak();

    // 加载历史记录文件
    loadHistory();

    // 加载配置文件
    detectConfig(pttConfigFilePath, ttpConfigFilePath);

    // 对话线程初始化
    workerThread = new QThread(this);
    worker = new ChatWorker();
    worker->moveToThread(workerThread);
    workerThread->start();

    // 语音识别线程初始化
    voiceWorker = new VoiceWorker(voiceModule);
    voiceThread = new QThread(this);
    voiceWorker->moveToThread(voiceThread);
    voiceThread->start();

    // 语音合成线程初始化
    voiceSpeakWorker = new VoiceSpeakWorker(voiceModule);
    voiceSpeakThread = new QThread(this);
    voiceSpeakWorker->moveToThread(voiceSpeakThread);
    voiceSpeakThread->start();

    // 信号槽连接
    connect(sendButton, &QPushButton::clicked, this, &ChatWindow::sendMessage);
    connect(inputLine, &QLineEdit::returnPressed, this, &ChatWindow::sendMessage); // enter发送
    connect(this, &ChatWindow::sendToWorker, worker, &ChatWorker::handleUserInput);
    connect(worker, &ChatWorker::responseReady, this, &ChatWindow::handleModelResponse);

    connect(historyList, &QListWidget::itemClicked, this, &ChatWindow::handleHistoryItemClicked);

    // 新建会话
    connect(newChatButton, &QPushButton::clicked, this, &ChatWindow::handleNewChat);

    // 语音输入
    connect(voiceButton, &QPushButton::clicked, this, &ChatWindow::handleVoiceInput);

    // 启用RAG
    connect(ragButton, &QPushButton::clicked, this, &ChatWindow::handleRagButton);

    // 语音识别信号连接
    connect(this, &ChatWindow::startVoiceRecognition, voiceWorker, &VoiceWorker::doRecognition);
    connect(voiceWorker, &VoiceWorker::voiceRecognized, this, &ChatWindow::handleVoiceResult);

    // 语音合成信号连接
    connect(this, &ChatWindow::startVoiceSpeak, voiceSpeakWorker, &VoiceSpeakWorker::speak);
}

ChatWindow::~ChatWindow()
{
    voiceSpeakThread->quit();
    voiceSpeakThread->wait();
    delete voiceSpeakWorker;
    delete voiceSpeakThread;

    voiceThread->quit();
    voiceThread->wait();
    delete voiceWorker;
    delete voiceThread;

    workerThread->quit();
    workerThread->wait();

    delete workerThread;

    delete voiceModule;

    delete worker;
}

void ChatWindow::sendMessage()
{
    QString userText = inputLine->text();
    if (userText.isEmpty())
        return;

    addChatBubble(userText, true);

    inputLine->clear();

    modelStateAndTextBubble = addChatBubble(false); // new 出来的东西，接收一下，当然，这里只是文本发送按钮对应的，还得给语音输入加上

    if (ragFlag == true)
    {

        RAGSearch rags;
        QString enhanced_query = QString::fromStdString(rags.get_RAG_Handeled_Text(userText.toStdString(), searchedFiles));
        // std::cout << enhanced_query.toStdString() << endl;
        emit sendToWorker(enhanced_query);
    }
    else
    {
        emit sendToWorker(userText);
    }
}

void ChatWindow::handleModelResponse(const QString &response)
{
    // addChatBubble(response, false);

    resetModelResponseState(modelStateAndTextBubble.modelStates); // 修改模型的回复状态

    addTextToBubble(modelStateAndTextBubble.texxtBubble, response); // 修改聊天气泡的文本内容
    // 这里只负责对之前已经创建好的内容进行修改，因为这个函数里面已经new了内容了，
    // 不能重复new

    // chatDisplay->append("模型：\n" + response);

    qApp->processEvents(); // 刷新界面

    if (ragFlag == true)
    {
        loadSearchedFilses();
    }

    // 语音输出
    if (voiceFlag)
    {
        voiceFlag = false; // 重置语音标志

        emit startVoiceSpeak(response);

        // voiceModule->speak_out(response.toStdString());
    }
}

void ChatWindow::handleHistoryChat()
{
    inputLine->clear();

    clearChatBubbles(); // 清理聊天气泡区域

    if (worker != nullptr) // 说明是从普通对话切过来的
    {
        // chatDisplay->append("---已切换至历史的聊天会话---");
        if (workerThread)
        {
            workerThread->quit();
            workerThread->wait();
            delete worker;
            delete workerThread;
            worker = nullptr;
            workerThread = nullptr;
        }
    }
    else if (historyworker != nullptr) // 说明是历史消息相互切换
    {
        // chatDisplay->append("---已切换至新的历史聊天会话---");
        if (workerThread)
        {
            workerThread->quit();
            workerThread->wait();
            delete historyworker;
            delete workerThread;
            historyworker = nullptr;
            workerThread = nullptr;
        }
    }
    else
    {
    }

    // 创建新的线程和历史聊天工作器对象
    workerThread = new QThread(this);
    historyworker = new HistoryWorker(historyFilePath);

    historyworker->moveToThread(workerThread);
    workerThread->start();

    loadHistory(); // 重新加载历史记录

    // 重新建立信号槽连接
    connect(sendButton, &QPushButton::clicked, this, &ChatWindow::sendMessage);
    connect(this, &ChatWindow::sendToWorker, historyworker, &HistoryWorker::handleUserInput);
    connect(historyworker, &HistoryWorker::responseReady, this, &ChatWindow::handleModelResponse);
}

void ChatWindow::handleNewChat()
{
    inputLine->clear();

    // 清空现有的聊天气泡
    clearChatBubbles();

    // 添加一个初始的气泡
    addChatBubble("你好，请问有什么需要我帮忙的吗？", false);

    // 1. 安全释放现有线程和 worker
    if (workerThread)
    {
        workerThread->quit();
        workerThread->wait();
        delete worker;
        delete workerThread;
        worker = nullptr;
        workerThread = nullptr;
    }

    // 2. 创建新的线程和 worker 对象
    workerThread = new QThread(this);
    worker = new ChatWorker();

    // 3. 将 worker 移动到新线程
    worker->moveToThread(workerThread);
    workerThread->start();

    loadHistory(); // 重新加载历史记录

    // 4. 重新建立信号槽连接
    connect(sendButton, &QPushButton::clicked, this, &ChatWindow::sendMessage);
    connect(this, &ChatWindow::sendToWorker, worker, &ChatWorker::handleUserInput);
    connect(worker, &ChatWorker::responseReady, this, &ChatWindow::handleModelResponse);
}

void ChatWindow::handleVoiceInput()
{
    voiceFlag = true; // 开启语音标志
    inputLine->setText("正在聆听中...");
    showListeningPopup(true);     // 主线程弹窗
    qApp->processEvents();        // 刷新界面
    emit startVoiceRecognition(); // 在后台线程中识别

    // modelStateAndTextBubble = addChatBubble(false); // 语音这里也要new一个，这个new指的是new的聊天气泡，这个接收的返回值只是覆盖指针地址，
    // //刚才测试语音忘了没加出现段错误了（当然是新运行一下程序的时候，因为没有前面普通聊天给这个全局变量加入内容的时候）

}

void ChatWindow::handleVoiceResult(const QString &result)
{
    inputLine->clear();

    if (result.isEmpty() || result == "\n")
    {
        showListeningPopup(false); // 显示“没听到”

        qApp->processEvents();
        return;
    }

    addChatBubble(result, true);
    modelStateAndTextBubble = addChatBubble(false);// 语音这里也要new一个，这个new指的是new的聊天气泡，这个接收的返回值只是覆盖指针地址，
    // //刚才测试语音忘了没加出现段错误了（当然是新运行一下程序的时候，因为没有前面普通聊天给这个全局变量加入内容的时候）
    emit sendToWorker(result);
}

void ChatWindow::handleRagButton()
{
    // 切换 ragFlag 的状态
    ragFlag = !ragFlag;

    // 根据 ragFlag 的状态设置按钮的颜色
    if (ragFlag)
    {
        // 按钮颜色变深
        ragButton->setStyleSheet("background-color:rgb(145, 174, 228); color: white;"); // 深色状态
    }
    else
    {
        // 按钮颜色恢复浅色
        ragButton->setStyleSheet("background-color:rgb(255, 255, 255); color: black;"); // 浅色状态
    }
}

void ChatWindow::handleHistoryItemClicked(QListWidgetItem *item)
{
    if (!item)
        return;
    QString path = item->text();
    path = "../conversations/" + path + ".json";
    historyFilePath = path.toStdString(); // 刷新获得历史消息

    // 切换到历史聊天会话
    handleHistoryChat();

    // std::cout << "history item clicked: " << historyFilePath << std::endl;

    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        // chatDisplay->append("---无法打开该历史文件!---");
        return;
    }

    QByteArray jsonData = file.readAll();
    file.close();

    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);
    if (parseError.error != QJsonParseError::NoError || !doc.isArray())
    {
        // chatDisplay->append("---JSON格式错误!---" + parseError.errorString());
        return;
    }

    QJsonArray historyArray = doc.array();
    for (const QJsonValue &entry : historyArray)
    {
        if (!entry.isObject())
            continue;
        QJsonObject obj = entry.toObject();
        QString userText = obj.value("user").toString();
        QString assistantText = obj.value("assistant").toString();
        if (!userText.isEmpty())
        {
            addChatBubble(userText, true);
        }

        // chatDisplay->append("用户：" + userText);
        if (!assistantText.isEmpty())
        {
            // std::cout << assistantText.toStdString() << std::endl;
            addChatBubble(assistantText, false);
        }

        // chatDisplay->append("模型：" + assistantText);
    }
}

void ChatWindow::loadHistory()
{
    Tools tool;
    QString path = "../conversations/"; // 假设历史记录文件存放在这个目录
    std::vector<std::string> files = tool.getFileNames(path.toStdString());

    // 清空 historyList 中的所有项
    historyList->clear();

    for (const std::string &filename : files)
    {
        QString fullPath = QDir(path).filePath(QString::fromStdString(filename));
        QFileInfo fileInfo(fullPath);
        QString baseName = fileInfo.baseName(); // 获取不带后缀的文件名
        QString suffix = fileInfo.suffix();     // 获取文件后缀名
        if (suffix == "json")
        {
            QListWidgetItem *item = new QListWidgetItem(baseName);
            historyList->addItem(item);
            // item->setToolTip(baseName);/// 设置工具提示为完整的文件名称
        }
    }
}

void ChatWindow::loadSearchedFilses()
{

    selectedFileList->clear();

    for (string str : searchedFiles)
    {
        QListWidgetItem *item = new QListWidgetItem(QString::fromStdString(str));
        selectedFileList->addItem(item);
        item->setToolTip(QString::fromStdString(str)); /// 设置工具提示为完整的文件名称
    }
}

void ChatWindow::addChatBubble(const QString &text, bool isUser)
{
    // 创建一个水平布局来组合头像和文本
    QHBoxLayout *bubbleLayout = new QHBoxLayout;

    // 创建头像 QLabel
    QLabel *avatar = addAvatar(isUser);

    // 创建文本 QLabel
    QLabel *bubbleText = addBubble(isUser);

    addTextToBubble(bubbleText, text);

    // 创建一个 QVBoxLayout 来嵌套头像和文本框
    QVBoxLayout *avatarAndTextLayout = new QVBoxLayout;
    avatarAndTextLayout->setSpacing(0);                  // 设置间距为 0
    avatarAndTextLayout->setContentsMargins(0, 0, 0, 0); // 设置边距为 0

    // 创建一个 QHBoxLayout 来组合头像和文本框
    QHBoxLayout *avatarPart = new QHBoxLayout;
    avatarPart->setSpacing(0);                  // 设置间距为 0
    avatarPart->setContentsMargins(0, 0, 0, 0); // 设置边距为 0

    // 根据用户或模型调整头像的位置
    if (isUser)
    {
        avatarPart->addStretch(); // 用户头像靠右展示
        avatarPart->addWidget(avatar);
    }
    else
    {
        avatarPart->addWidget(avatar);
        avatarPart->addStretch(); // 模型头像靠左展示
    }

    // 将头像和文本框添加到 QVBoxLayout 中
    avatarAndTextLayout->addLayout(avatarPart);
    avatarAndTextLayout->addWidget(bubbleText);

    // 将 QVBoxLayout 添加到水平布局中
    if (isUser)
    {
        bubbleLayout->addStretch(); // 用户消息靠右显示
        bubbleLayout->addLayout(avatarAndTextLayout);
    }
    else
    {
        bubbleLayout->addLayout(avatarAndTextLayout);
        bubbleLayout->addStretch(); // 模型消息靠左显示
    }

    // 创建一个 QWidget 来容纳水平布局
    QWidget *bubbleWidget = new QWidget;
    bubbleWidget->setLayout(bubbleLayout);

    // 将组合的 QWidget 添加到聊天布局中
    chatDisplay->addWidget(bubbleWidget);
    chatBubbles.append(bubbleText); // 将文本气泡添加到列表中

    // 自动滚动到最底部
    QScrollBar *scrollBar = scrollArea->verticalScrollBar();
    if (scrollBar)
    {
        QTimer::singleShot(0, scrollBar, [scrollBar]()
                           { scrollBar->setValue(scrollBar->maximum()); });
    }
}

ModelStateAndTextBubble ChatWindow::addChatBubble(bool isUser)
{
    // 创建一个水平布局来组合头像和文本
    QHBoxLayout *bubbleLayout = new QHBoxLayout;

    // 创建头像 QLabel
    QLabel *avatar = addAvatar(isUser);

    // 创建文本 QLabel
    QLabel *textBubble = addBubble(isUser);

    QLabel *modelResponseState = new QLabel;

    ModelStateAndTextBubble collects; // 收集两部分内容，用来后续的修改

    collects.modelStates = modelResponseState;
    collects.texxtBubble = textBubble;

    modelResponseState->setText("正在思考中...");

    // 创建一个 QVBoxLayout 来嵌套头像和文本框
    QVBoxLayout *avatarAndTextLayout = new QVBoxLayout;
    avatarAndTextLayout->setSpacing(0);                  // 设置间距为 0
    avatarAndTextLayout->setContentsMargins(0, 0, 0, 0); // 设置边距为 0

    // 放置模型头像和模型输出状态
    QHBoxLayout *avatarPart = new QHBoxLayout;
    avatarPart->setSpacing(3); // 设置间距为 3

    // 将头像部分填充
    avatarPart->addWidget(avatar);
    avatarPart->addWidget(modelResponseState);

    // 将头像部分和文本框添加到 QVBoxLayout 中
    avatarAndTextLayout->addLayout(avatarPart);
    avatarAndTextLayout->addWidget(textBubble);

    // 将 QVBoxLayout 添加到水平布局中
    if (isUser)
    {
        bubbleLayout->addStretch(); // 用户消息靠右显示
        bubbleLayout->addLayout(avatarAndTextLayout);
    }
    else
    {
        bubbleLayout->addLayout(avatarAndTextLayout);
        bubbleLayout->addStretch(); // 模型消息靠左显示
    }

    // 创建一个 QWidget 来容纳水平布局
    QWidget *bubbleWidget = new QWidget;
    bubbleWidget->setLayout(bubbleLayout);

    // 将组合的 QWidget 添加到聊天布局中
    chatDisplay->addWidget(bubbleWidget);
    chatBubbles.append(textBubble); // 将文本气泡添加到列表中

    // 自动滚动到最底部
    QScrollBar *scrollBar = scrollArea->verticalScrollBar();
    if (scrollBar)
    {
        QTimer::singleShot(0, scrollBar, [scrollBar]()
                           { scrollBar->setValue(scrollBar->maximum()); });
    }
    return collects;
}

void ChatWindow::clearChatBubbles()
{
    // 遍历 chatDisplay 中的所有控件
    QLayoutItem *child;
    while ((child = chatDisplay->takeAt(0)) != nullptr)
    {
        if (child->widget())
        {
            // 如果是控件，调用 deleteLater 安全删除
            child->widget()->deleteLater();
        }
        // 删除布局项
        delete child;
    }
}

QLabel *ChatWindow::addBubble(bool isUser)
{
    QLabel *bubbleText = new QLabel;                        //(text);
    bubbleText->setWordWrap(true);                          // 自动换行
    bubbleText->setAlignment(Qt::AlignTop | Qt::AlignLeft); // 文本对齐方式
    bubbleText->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    if (isUser)
    {
        // 用户消息的样式
        bubbleText->setStyleSheet("background-color:rgb(60, 218, 204); border-radius: 10px; padding: 5px;");
    }
    else
    {
        // 模型消息的样式
        bubbleText->setStyleSheet("background-color:rgb(238, 231, 231); border-radius: 10px; padding: 5px;");
    }

    return bubbleText;
}

QLabel *ChatWindow::addAvatar(bool isUser)
{
    QLabel *avatar = new QLabel;
    avatar->setFixedSize(40, 40);    // 设置头像的固定大小
    avatar->setScaledContents(true); // 确保图片会缩放以适应 QLabel

    if (isUser)
    {
        // 用户头像
        avatar->setPixmap(QPixmap("../assets/用户.png")); // 替换为用户头像路径
        avatar->setStyleSheet("border-radius: 20px;");    // 设置头像为圆形
    }
    else
    {
        // 模型头像
        avatar->setPixmap(QPixmap("../assets/智能机器人.png")); // 替换为模型头像路径
        avatar->setStyleSheet("border-radius: 20px;");          // 设置头像为圆形
    }

    return avatar;
}

void ChatWindow::addTextToBubble(QLabel *&Bubble, QString text)
{   
    
    Bubble->setText(text);
}

void ChatWindow::resetModelResponseState(QLabel *&modelResponseState)
{
    modelResponseState->setText("思考完成");
}

void ChatWindow::showListeningPopup(bool linstenOrNoVoice)
{
    if (!listeningPopup)
    {
        listeningPopup = new QLabel(this);
    }

    if (linstenOrNoVoice)
    {
        listeningPopup->setText("正在聆听...");
    }
    else
    {
        listeningPopup->setText("抱歉我没有听到声音");
    }

    listeningPopup->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 160); color: white; padding: 10px; border-radius: 10px; font-size: 16px; }");
    listeningPopup->setAlignment(Qt::AlignCenter);
    listeningPopup->setFixedSize(200, 50);

    // 居中显示
    int x = (width() - listeningPopup->width()) / 2;
    int y = (height() - listeningPopup->height()) / 2;
    listeningPopup->move(x, y);

    listeningPopup->show();

    if (!popupTimer)
    {
        popupTimer = new QTimer(this);
        popupTimer->setSingleShot(true);
        connect(popupTimer, &QTimer::timeout, listeningPopup, &QLabel::hide);
    }

    popupTimer->start(2000); // 2秒后隐藏
}


// 读取配置文件
std::pair<QString, QString> ChatWindow::readConfig(const QString& filePath) {
    QString apiKey;
    QString secretKey;

    QFile file(filePath);
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QTextStream in(&file);
        while (!in.atEnd()) {
            QString line = in.readLine();
            if (line.startsWith("API_KEY")) {
                apiKey = line.split('=')[1].trimmed().remove('"');
            } else if (line.startsWith("SECRET_KEY")) {
                secretKey = line.split('=')[1].trimmed().remove('"');
            }
        }
        file.close();
    }

    return {apiKey, secretKey};
}

// 保存配置文件
void ChatWindow::saveConfig(const QString& filePath, const QString& apiKey, const QString& secretKey) {
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out << "API_KEY = \"" << apiKey << "\"\n";
        out << "SECRET_KEY = \"" << secretKey << "\"\n";
        file.close();
    }
}


// 检测配置文件
void ChatWindow::detectConfig(QString pttConfigFilePath, QString ttpConfigFilePath){
    auto [apiKey1, secretKey1] = readConfig(pttConfigFilePath);
    auto [apiKey2, secretKey2] = readConfig(ttpConfigFilePath);

    // 检查 API_KEY 和 SECRET_KEY 是否为空
    if (apiKey1.isEmpty() || secretKey1.isEmpty() || apiKey2.isEmpty() || secretKey2.isEmpty()) {
        ConfigDialog dialog(this);
        if (dialog.exec() == QDialog::Accepted) {
            apiKey1 = dialog.getApiKey1();
            secretKey1 = dialog.getSecretKey1();
            apiKey2 = dialog.getApiKey2();
            secretKey2 = dialog.getSecretKey2();

            // 保存到配置文件
            saveConfig(pttConfigFilePath, apiKey1, secretKey1);
            saveConfig(ttpConfigFilePath, apiKey2, secretKey2);
        } else {
            QMessageBox::warning(this, "配置未完成", "未输入完整的配置信息，程序将退出。");
            exit(1);  // 退出程序
        }
    }
}
    